package com.smart.milvus.model.metadata;

import com.smart.milvus.annotation.DynamicCollection;
import com.smart.milvus.annotation.MilvusCollection;
import com.smart.milvus.annotation.MilvusColumn;
import com.smart.milvus.annotation.NonMilvusColumn;
import com.smart.milvus.autoconfigure.MilvusProperties;
import com.smart.milvus.base.AnnotationHandler;
import com.smart.milvus.base.DynamicCollectionHandler;
import com.smart.milvus.base.PostInitCollectionInfoHandler;
import com.smart.milvus.bean.DefaultDynamicHandler;
import com.smart.milvus.bean.DefaultSpElExecutor;
import com.smart.milvus.bean.GlobalConfig;
import com.smart.milvus.exception.MilvusException;
import com.smart.milvus.util.*;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>
 * 实体类反射表辅助类
 * </p>
 */
@Slf4j
public class CollectionHelper {

    /**
     * 储存反射类表信息
     */
    private static final Map<Class<?>, CollectionDefinition> COLLECTION_INFO_CACHE = new ConcurrentHashMap<>();

    /**
     * <p>
     * 获取实体映射表信息
     * </p>
     *
     * @param clazz 反射实体类
     * @return 数据库表反射信息
     */
    public static CollectionDefinition getCollectionInfo(Class<?> clazz) {
        if (clazz == null || clazz.isPrimitive() || SimpleTypeRegistry.isSimpleType(clazz) || clazz.isInterface()) {
            return null;
        }
        Class<?> targetClass = ClassUtils.getUserClass(clazz);
        CollectionDefinition definition = COLLECTION_INFO_CACHE.get(targetClass);
        if (null != definition) {
            return definition;
        }
        definition = initCollectionInfo(targetClass);

        COLLECTION_INFO_CACHE.put(targetClass, definition);
        return definition;
    }

    /**
     * <p>
     * 实体类反射获取表信息【初始化】
     * </p>
     *
     * @param clazz 反射实体类
     * @return 数据库表反射信息
     */
    public static synchronized CollectionDefinition initCollectionInfo(Class<?> clazz) {
        CollectionDefinition targetCollectionInfo = COLLECTION_INFO_CACHE.get(clazz);
        if (targetCollectionInfo != null && (clazz.equals(targetCollectionInfo.getEntityType()))) {
            return targetCollectionInfo;
        }
        GlobalConfig globalConfig = GlobalConfig.INSTANCE;

        PostInitCollectionInfoHandler postInitCollectionInfoHandler = globalConfig.getPostInitCollectionInfoHandler();
        /* 没有获取到缓存信息,则初始化 */
        CollectionDefinition definition = postInitCollectionInfoHandler.creteCollectionInfo(clazz);

        /* 初始化表名相关 */
        initCollectionName(clazz, definition);
        /* 初始化字段相关 */
        initCollectionFields(clazz, definition);

        /* 自动构建 resultMap */
        postInitCollectionInfoHandler.postCollectionInfo(definition);
        COLLECTION_INFO_CACHE.put(clazz, definition);
        /* 缓存 lambda */
        LambdaUtils.installCache(definition);
        return definition;
    }


    /**
     * <p>
     * 初始化 表数据库类型,表名,resultMap
     * </p>
     *
     * @param clazz          实体类
     * @param collectionInfo 数据库表反射信息
     */
    private static void initCollectionName(Class<?> clazz, CollectionDefinition collectionInfo) {
        /* 数据库全局配置 */
        GlobalConfig globalConfig = GlobalConfig.INSTANCE;
        MilvusProperties milvusProperties = globalConfig.getMilvusProperties();
        MilvusProperties.MilvusConfig dbConfig = milvusProperties.getGlobalConfig();
        AnnotationHandler annotationHandler = globalConfig.getAnnotationHandler();
        DefaultSpElExecutor defaultSpElExecutor = globalConfig.getDefaultSpElExecutor();

        collectionInfo.setEntityType(clazz);
        //设置默认名
        String collectionName = clazz.getSimpleName();
        collectionInfo.setDefaultName(initCollectionNameWithDbConfig(collectionName, dbConfig));
        // 表追加前缀
        String collectionPrefix = dbConfig.getCollectionPrefix();
        if (StringUtils.isNotBlank(collectionPrefix)) {
            collectionInfo.setPrefix(collectionPrefix);
        }
        // 表格式化
        String collectionFormat = dbConfig.getCollectionFormat();
        if (StringUtils.isNotBlank(collectionFormat)) {
            collectionInfo.setFormat(collectionFormat);
        }

        DynamicCollection dynamicCollection = annotationHandler.getAnnotation(clazz, DynamicCollection.class);
        MilvusCollection vectorCollectionName = annotationHandler.getAnnotation(clazz, MilvusCollection.class);
        if (dynamicCollection != null && vectorCollectionName != null) {
            throw new MilvusException("DynamicCollection and MilvusCollection cannot be used at the same time!");
        }

        if (dynamicCollection != null) {
            collectionInfo.setDescription(dynamicCollection.description());
            DynamicCollectionHandler dynamicCollectionHandler = globalConfig.getDynamicCollectionHandler();
            if (dynamicCollectionHandler != null) {
                collectionInfo.setCollectionHandler(dynamicCollectionHandler);
            } else {
                collectionInfo.setCollectionHandler(new DefaultDynamicHandler(defaultSpElExecutor, dynamicCollection));
            }
            return;
        }

        if (vectorCollectionName != null) {
            collectionInfo.setName(vectorCollectionName.name());
            collectionInfo.setDescription(vectorCollectionName.description());
            collectionInfo.setDatabase(vectorCollectionName.database());
        }
    }

    /**
     * <p>
     * 初始化 表主键,表字段
     * </p>
     *
     * @param clazz                实体类
     * @param collectionDefinition 数据库表反射信息
     */
    private static void initCollectionFields(Class<?> clazz, CollectionDefinition collectionDefinition) {
        GlobalConfig globalConfig = GlobalConfig.INSTANCE;
        AnnotationHandler annotationHandler = globalConfig.getAnnotationHandler();
        PostInitCollectionInfoHandler postInitCollectionInfoHandler = globalConfig.getPostInitCollectionInfoHandler();
        List<Field> list = ReflectionKit.getFieldList(ClassUtils.getUserClass(clazz));

        List<ColumnDefinition> columnsList = new ArrayList<>(list.size());
        for (Field field : list) {
            if (annotationHandler.getAnnotation(field, NonMilvusColumn.class) != null) {
                continue;
            }
            final MilvusColumn collectionColumn = annotationHandler.getAnnotation(field, MilvusColumn.class);
            ColumnDefinition columnDefinition;
            if (collectionColumn != null) {
                columnDefinition = new ColumnDefinition(collectionDefinition, field, collectionColumn);
            } else {
                columnDefinition = new ColumnDefinition(collectionDefinition, field);
            }
            if (columnDefinition.isPrimary()) {
                collectionDefinition.setKeyColumn(columnDefinition.getName());
                collectionDefinition.setKeyProperty(field.getName());
                collectionDefinition.setKeyField(field);
            }
            columnsList.add(columnDefinition);
            postInitCollectionInfoHandler.postFieldInfo(columnDefinition);
        }

        collectionDefinition.setColumns(columnsList);
    }

    /**
     * 根据 DbConfig 初始化 表名
     *
     * @param className 类名
     * @param dbConfig  DbConfig
     * @return 表名
     */
    private static String initCollectionNameWithDbConfig(String className,  MilvusProperties.MilvusConfig dbConfig) {
        String collectionName = className;
        // 开启表名下划线申明
        if (dbConfig.isTableUnderline()) {
            collectionName = StringUtils.camelToUnderline(collectionName);
        }
        // 大写命名判断
        if (dbConfig.isCapitalMode()) {
            collectionName = collectionName.toUpperCase();
        } else {
            // 首字母小写
            collectionName = StringUtils.firstToLowerCase(collectionName);
        }
        return collectionName;
    }
}
